/**
 * @file combine.cpp
 * Copyright (c) 2020 Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2020-12-30
 * @version : 1.0.0.0
 * @brief   : 测试 PDFium 库 ：将多张 JPEG 图片合成到 PDF 文件。
 */

#include "xtypes.h"
#include "XJPEG_wrapper.h"
#include "XPDF_wrapper.h"

#include <list>
#include <string>

////////////////////////////////////////////////////////////////////////////////

/**********************************************************/
/**
 * @brief 字符串忽略大小写的比对操作。
 *
 * @param [in ] xszt_lcmp : 比较操作的左值字符串。
 * @param [in ] xszt_rcmp : 比较操作的右值字符串。
 *
 * @return int
 *         - xszt_lcmp <  xszt_rcmp，返回 <= -1；
 *         - xszt_lcmp == xszt_rcmp，返回 ==  0；
 *         - xszt_lcmp >  xszt_rcmp，返回 >=  1；
 */
static int xstr_icmp(const char * xszt_lcmp, const char * xszt_rcmp)
{
    int xit_lvalue = 0;
    int xit_rvalue = 0;

    if (xszt_lcmp == xszt_rcmp)
        return 0;
    if (NULL == xszt_lcmp)
        return -1;
    if (NULL == xszt_rcmp)
        return 1;

    do
    {
        if (((xit_lvalue = (*(xszt_lcmp++))) >= 'A') && (xit_lvalue <= 'Z'))
            xit_lvalue -= ('A' - 'a');

        if (((xit_rvalue = (*(xszt_rcmp++))) >= 'A') && (xit_rvalue <= 'Z'))
            xit_rvalue -= ('A' - 'a');

    } while (xit_lvalue && (xit_lvalue == xit_rvalue));

    return (xit_lvalue - xit_rvalue);
}

/**********************************************************/
/**
 * @brief 从文件路径名中，获取 文件扩展名 字符串的起始位置。
 * 
 * @param [in ] xszt_fpath   : 文件路径名。
 * @param [in ] xbt_with_dot : 返回的 文件扩展名，是否带上 “.” 分割号。
 * 
 * @return x_cstring_t : 文件扩展名。
 */
x_cstring_t file_ext_name(x_cstring_t xszt_fpath, x_bool_t xbt_with_dot)
{
    x_char_t * xct_iter = (x_char_t *)xszt_fpath;
    x_char_t * xct_vpos = X_NULL;

    if (X_NULL == xszt_fpath)
    {
        return X_NULL;
    }
    else if ('.' == xszt_fpath[0])
    {
        // "." or ".." will return to tail.
        if ('\0' == xszt_fpath[1])
            return xszt_fpath + 1;
        else if (('.' == xszt_fpath[1]) && ('\0' == xszt_fpath[2]))
            return xszt_fpath + 2;
    }

    while (*xct_iter)
    {
        if ('.' == *xct_iter)
            xct_vpos = xct_iter + 1;
        xct_iter += 1;
    }

    if (X_NULL == xct_vpos)
        xct_vpos = xct_iter;
    else if (xbt_with_dot)
        xct_vpos -= 1;

    return (x_cstring_t)xct_vpos;
}

////////////////////////////////////////////////////////////////////////////////

std::string              xstr_output;
std::list< std::string > xlst_images;

////////////////////////////////////////////////////////////////////////////////

#include <Windows.h>

x_void_t import_from_dir(x_cstring_t xszt_dir)
{
    x_cstring_t xszt_ext  = X_NULL;
    std::string xstr_path = xszt_dir;
    if (('\\' != xstr_path.at(xstr_path.size() - 1)) &&
        ('/'  != xstr_path.at(xstr_path.size() - 1)))
    {
        xstr_path.append(1, '/');
    }

    std::string xstr_dir = xstr_path + std::string("*.*");
    
    WIN32_FIND_DATAA xFindData;
    memset(&xFindData, 0, sizeof(WIN32_FIND_DATAA));

    HANDLE hFind = FindFirstFileA(xstr_dir.c_str(), &xFindData);
    if (INVALID_HANDLE_VALUE == hFind)
    {
        return;
    }

    do
    {
        // ignore "." and ".."
        if ('.' == xFindData.cFileName[0])
        {
            if (( '\0' == xFindData.cFileName[1]) ||
                (('.'  == xFindData.cFileName[1]) &&
                 ('\0' == xFindData.cFileName[2])))
            {
                continue;
            }
        }

        if (xFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            continue;
        }

        xszt_ext = file_ext_name(xFindData.cFileName, X_FALSE);
        if ((X_NULL != xszt_ext) &&
            ((0 == xstr_icmp(xszt_ext, "jpg" )) ||
             (0 == xstr_icmp(xszt_ext, "jpeg"))))
        {
            xlst_images.push_back(xstr_path + std::string(xFindData.cFileName));
        }
    } while (FindNextFileA(hFind, &xFindData));

    FindClose(hFind);
}

////////////////////////////////////////////////////////////////////////////////

x_bool_t args_init(x_int32_t xit_argc, x_char_t * xct_argv[])
{
    if (xit_argc < 4)
    {
        return X_FALSE;
    }

    typedef enum __args_status__
    {
        XARGS_UNKNOW,
        XARGS_OUTPUT,
        XARGS_DIRECTORY
    } xargs_status_t;

    xargs_status_t xargs_status = XARGS_UNKNOW;

    for (x_int32_t xit_iter = 1; xit_iter < xit_argc; ++xit_iter)
    {
        switch (xargs_status)
        {
        case XARGS_UNKNOW:
            {
                if (0 == xstr_icmp(xct_argv[xit_iter], "-o"))
                {
                    xargs_status = XARGS_OUTPUT;
                }
                else if (0 == xstr_icmp(xct_argv[xit_iter], "-d"))
                {
                    xargs_status = XARGS_DIRECTORY;
                }
                else
                {
                    x_cstring_t xszt_ext =
                            file_ext_name(xct_argv[xit_iter], X_FALSE);

                    if ((X_NULL != xszt_ext) &&
                        ((0 == xstr_icmp(xszt_ext, "jpg" )) ||
                         (0 == xstr_icmp(xszt_ext, "jpeg"))))
                    {
                        xlst_images.push_back(std::string(xct_argv[xit_iter]));
                    }
                    else
                    {
                        printf("unknow arg : %s\n", xct_argv[xit_iter]);
                    }
                }
            }
            break;

        case XARGS_OUTPUT:
            {
                xstr_output = xct_argv[xit_iter];
                xargs_status = XARGS_UNKNOW;
            }
            break;

        case XARGS_DIRECTORY:
            {
                import_from_dir(xct_argv[xit_iter]);
                xargs_status = XARGS_UNKNOW;
            }
            break;

        default:
            break;
        }
    }

    if (xstr_output.empty() || xlst_images.empty())
    {
        return X_FALSE;
    }

    return X_TRUE;
}

x_void_t usage(x_cstring_t xszt_argv0)
{
    printf("usage:                                                                 \n"
           " %s -o < output > [ input: -d < directory > ] [ list: 1.jpg 2.jpg ... ]\n"
           "    output : the output pdf file .                                     \n"
           "    input  : list of image files imported from directory .             \n"
           "    list   : list of image files .                                     \n",
           xszt_argv0);
}

////////////////////////////////////////////////////////////////////////////////

int main(int argc, char * argv[])
{
    x_int32_t xit_err = X_ERR_UNKNOW;
    x_errno_t xerr_no = X_ERR_UNKNOW;

    //======================================

    if (0 != XPDF_load("pdfium.dll", X_NULL))
    {
        printf("XPDF_load() failed!\n");
        return -1;
    }

    //======================================

    do
    {
        //======================================

        if (!args_init(argc, argv))
        {
            usage(argv[0]);
            break;
        }

        //======================================

        XPDF_writer_t xpdf_writer;

        xerr_no = xpdf_writer.create(X_NULL);
        if (XERR_FAILED(xerr_no))
        {
            printf("xpdf_writer.create(X_NULL) return error : %s\n",
                   XPDF_writer_t::xerrno_name(xerr_no));
            break;
        }

        //======================================

        jpeg_info_t   jinfo;
        jdec_handle_t jdecoder;

        x_int32_t xit_page_no = 0;

        for (std::list< std::string >::iterator
             xiter = xlst_images.begin();
             xiter != xlst_images.end();
             ++xiter)
        {
            jdecoder.config_src(JCTRL_MODE_FILE, (j_handle_t)xiter->c_str(), 0);

            xit_err = jdecoder.src_info(&jinfo);
            if (JDEC_ERR_OK != xit_err)
            {
                printf("jdecoder.src_info(&jinfo) return error [%s], file : %s\n",
                       jdec_errno_name(xit_err), xiter->c_str());
                continue;
            }

            xerr_no = xpdf_writer.insert_page_by_jpegfile(
                xit_page_no, xiter->c_str(), jinfo.jit_width, jinfo.jit_height);
            if (XERR_FAILED(xerr_no))
            {
                printf("xpdf_writer.insert_page_by_jpegfile("
                       "[%d], [%s], [%d], [%d]) return error : %s\n",
                       xit_page_no,
                       xiter->c_str(),
                       jinfo.jit_width,
                       jinfo.jit_height,
                       XPDF_writer_t::xerrno_name(xerr_no));
                continue;
            }

            printf("insert jpeg file: [%d, %d] %s\n",
                   jinfo.jit_width, jinfo.jit_height, xiter->c_str());
            xit_page_no += 1;
        }

        //======================================

        xerr_no = xpdf_writer.save_to_file(xstr_output.c_str());
        if (XERR_FAILED(xerr_no))
        {
            printf("xpdf_writer.save_to_file([%s]) return error : %s\n",
                    xstr_output.c_str(),
                    XPDF_writer_t::xerrno_name(xerr_no));
            break;
        }

        //======================================
    } while (0);

    //======================================

    XPDF_unload();

    //======================================

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
